home *** CD-ROM | disk | FTP | other *** search
/ PC World 2006 November / PCWorld_2006-11_cd.bin / domacnost a kancelar / findgraph / fgraph.exe / {app} / TestVC / Subclass.cpp < prev    next >
C/C++ Source or Header  |  2002-08-09  |  7KB  |  271 lines

  1. ////////////////////////////////////////////////////////////////
  2. // Copyright 1998 Paul DiLascia
  3. // If this code works, it was written by Paul DiLascia.
  4. // If not, I don't know who wrote it.
  5. //
  6. // CSubclassWnd is a generic class for hooking another window's messages.
  7.  
  8. #include "StdAfx.h"
  9. #include "Subclass.h"
  10.  
  11. #ifdef _DEBUG
  12. #define new DEBUG_NEW
  13. #undef THIS_FILE
  14. static char THIS_FILE[] = __FILE__;
  15. #endif
  16.  
  17. //////////////////
  18. // The message hook map is derived from CMapPtrToPtr, which associates
  19. // a pointer with another pointer. It maps an HWND to a CSubclassWnd, like
  20. // the way MFC's internal maps map HWND's to CWnd's. The first CSubclassWnd
  21. // attached to a window is stored in the map; all other CSubclassWnd's for that
  22. // window are then chained via CSubclassWnd::m_pNext.
  23. //
  24. class CSubclassWndMap : private CMapPtrToPtr {
  25. public:
  26.     CSubclassWndMap();
  27.     ~CSubclassWndMap();
  28.     static CSubclassWndMap& GetHookMap();
  29.     void Add(HWND hwnd, CSubclassWnd* pSubclassWnd);
  30.     void Remove(CSubclassWnd* pSubclassWnd);
  31.     void RemoveAll(HWND hwnd);
  32.     CSubclassWnd* Lookup(HWND hwnd);
  33. };
  34.  
  35. // This trick is used so the hook map isn't
  36. // instantiated until someone actually requests it.
  37. //
  38. #define    theHookMap    (CSubclassWndMap::GetHookMap())
  39.  
  40. IMPLEMENT_DYNAMIC(CSubclassWnd, CWnd);
  41.  
  42. CSubclassWnd::CSubclassWnd()
  43. {
  44.     m_pNext = NULL;
  45.     m_pOldWndProc = NULL;    
  46.     m_hWnd  = NULL;
  47. }
  48.  
  49. CSubclassWnd::~CSubclassWnd()
  50. {
  51.     if (m_hWnd) 
  52.         HookWindow((HWND)NULL);        // unhook window
  53. }
  54.  
  55. //////////////////
  56. // Hook a window.
  57. // This installs a new window proc that directs messages to the CSubclassWnd.
  58. // pWnd=NULL to remove.
  59. //
  60. BOOL CSubclassWnd::HookWindow(HWND hwnd)
  61. {
  62.     ASSERT_VALID(this);
  63.     if (hwnd) {
  64.         // Hook the window
  65.         ASSERT(m_hWnd==NULL);
  66.         ASSERT(::IsWindow(hwnd));
  67.         theHookMap.Add(hwnd, this);            // Add to map of hooks
  68.  
  69.     } else if (m_hWnd) {
  70.         // Unhook the window
  71.         theHookMap.Remove(this);                // Remove from map
  72.         m_pOldWndProc = NULL;
  73.     }
  74.     m_hWnd = hwnd;
  75.     return TRUE;
  76. }
  77.  
  78. //////////////////
  79. // Window proc-like virtual function which specific CSubclassWnds will
  80. // override to do stuff. Default passes the message to the next hook; 
  81. // the last hook passes the message to the original window.
  82. // You MUST call this at the end of your WindowProc if you want the real
  83. // window to get the message. This is just like CWnd::WindowProc, except that
  84. // a CSubclassWnd is not a window.
  85. //
  86. LRESULT CSubclassWnd::WindowProc(UINT msg, WPARAM wp, LPARAM lp)
  87. {
  88. //    ASSERT_VALID(this);  // removed for speed
  89.     ASSERT(m_pOldWndProc);
  90.     return m_pNext ? m_pNext->WindowProc(msg, wp, lp) :    
  91.         ::CallWindowProc(m_pOldWndProc, m_hWnd, msg, wp, lp);
  92. }
  93.  
  94. //////////////////
  95. // Like calling base class WindowProc, but with no args, so individual
  96. // message handlers can do the default thing. Like CWnd::Default
  97. //
  98. LRESULT CSubclassWnd::Default()
  99. {
  100.     // MFC stores current MSG in thread state
  101.     MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
  102.     // Note: must explicitly call CSubclassWnd::WindowProc to avoid infinte
  103.     // recursion on virtual function
  104.     return CSubclassWnd::WindowProc(curMsg.message, curMsg.wParam, curMsg.lParam);
  105. }
  106.  
  107. #ifdef _DEBUG
  108. void CSubclassWnd::AssertValid() const
  109. {
  110.     CObject::AssertValid();
  111.     ASSERT(m_hWnd==NULL || ::IsWindow(m_hWnd));
  112.     if (m_hWnd) {
  113.         for (CSubclassWnd* p = theHookMap.Lookup(m_hWnd); p; p=p->m_pNext) {
  114.             if (p==this)
  115.                 break;
  116.         }
  117.         ASSERT(p); // should have found it!
  118.     }
  119. }
  120.  
  121. void CSubclassWnd::Dump(CDumpContext& dc) const
  122. {
  123.     CObject::Dump(dc);
  124. }
  125.  
  126. #endif
  127.  
  128. //////////////////
  129. // Subclassed window proc for message hooks. Replaces AfxWndProc (or whatever
  130. // else was there before.)
  131. //
  132. LRESULT CALLBACK
  133. HookWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
  134. {
  135. #ifdef _USRDLL
  136.     // If this is a DLL, need to set up MFC state
  137.     AFX_MANAGE_STATE(AfxGetStaticModuleState());
  138. #endif
  139.  
  140.     // Set up MFC message state just in case anyone wants it
  141.     // This is just like AfxCallWindowProc, but we can't use that because
  142.     // a CSubclassWnd is not a CWnd.
  143.     //
  144.     MSG& curMsg = AfxGetThreadState()->m_lastSentMsg;
  145.     MSG  oldMsg = curMsg;   // save for nesting
  146.     curMsg.hwnd        = hwnd;
  147.     curMsg.message = msg;
  148.     curMsg.wParam  = wp;
  149.     curMsg.lParam  = lp;
  150.  
  151.     // Get hook object for this window. Get from hook map
  152.     CSubclassWnd* pSubclassWnd = theHookMap.Lookup(hwnd);
  153.     ASSERT(pSubclassWnd);
  154.  
  155.     LRESULT lr;
  156.     if (msg==WM_NCDESTROY) {
  157.         // Window is being destroyed: unhook all hooks (for this window)
  158.         // and pass msg to orginal window proc
  159.         //
  160.         WNDPROC wndproc = pSubclassWnd->m_pOldWndProc;
  161.         theHookMap.RemoveAll(hwnd);
  162.         lr = ::CallWindowProc(wndproc, hwnd, msg, wp, lp);
  163.  
  164.     } else {
  165.         // pass to msg hook
  166.         lr = pSubclassWnd->WindowProc(msg, wp, lp);
  167.     }
  168.     curMsg = oldMsg;            // pop state
  169.     return lr;
  170. }
  171.  
  172. ////////////////////////////////////////////////////////////////
  173. // CSubclassWndMap implementation
  174. //
  175. CSubclassWndMap::CSubclassWndMap()
  176. {
  177. }
  178.  
  179. CSubclassWndMap::~CSubclassWndMap()
  180. {
  181. // This assert bombs when posting WM_QUIT, so I've deleted it.
  182. //    ASSERT(IsEmpty());    // all hooks should be removed!    
  183. }
  184.  
  185. //////////////////
  186. // Get the one and only global hook map
  187. // 
  188. CSubclassWndMap& CSubclassWndMap::GetHookMap()
  189. {
  190.     // By creating theMap here, C++ doesn't instantiate it until/unless
  191.     // it's ever used! This is a good trick to use in C++, to
  192.     // instantiate/initialize a static object the first time it's used.
  193.     //
  194.     static CSubclassWndMap theMap;
  195.     return theMap;
  196. }
  197.  
  198. /////////////////
  199. // Add hook to map; i.e., associate hook with window
  200. //
  201. void CSubclassWndMap::Add(HWND hwnd, CSubclassWnd* pSubclassWnd)
  202. {
  203.     ASSERT(hwnd && ::IsWindow(hwnd));
  204.  
  205.     // Add to front of list
  206.     pSubclassWnd->m_pNext = Lookup(hwnd);
  207.     SetAt(hwnd, pSubclassWnd);
  208.     
  209.     if (pSubclassWnd->m_pNext==NULL) {
  210.         // If this is the first hook added, subclass the window
  211.         pSubclassWnd->m_pOldWndProc = 
  212.             (WNDPROC)SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)HookWndProc);
  213.  
  214.     } else {
  215.         // just copy wndproc from next hook
  216.         pSubclassWnd->m_pOldWndProc = pSubclassWnd->m_pNext->m_pOldWndProc;
  217.     }
  218.     ASSERT(pSubclassWnd->m_pOldWndProc);
  219. }
  220.  
  221. //////////////////
  222. // Remove hook from map
  223. //
  224. void CSubclassWndMap::Remove(CSubclassWnd* pUnHook)
  225. {
  226.     HWND hwnd = pUnHook->m_hWnd;
  227.     ASSERT(hwnd && ::IsWindow(hwnd));
  228.  
  229.     CSubclassWnd* pHook = Lookup(hwnd);
  230.     ASSERT(pHook);
  231.     if (pHook==pUnHook) {
  232.         // hook to remove is the one in the hash table: replace w/next
  233.         if (pHook->m_pNext)
  234.             SetAt(hwnd, pHook->m_pNext);
  235.         else {
  236.             // This is the last hook for this window: restore wnd proc
  237.             RemoveKey(hwnd);
  238.             SetWindowLong(hwnd, GWL_WNDPROC, (DWORD)pHook->m_pOldWndProc);
  239.         }
  240.     } else {
  241.         // Hook to remove is in the middle: just remove from linked list
  242.         while (pHook->m_pNext!=pUnHook)
  243.             pHook = pHook->m_pNext;
  244.         ASSERT(pHook && pHook->m_pNext==pUnHook);
  245.         pHook->m_pNext = pUnHook->m_pNext;
  246.     }
  247. }
  248.  
  249. //////////////////
  250. // Remove all the hooks for a window
  251. //
  252. void CSubclassWndMap::RemoveAll(HWND hwnd)
  253. {
  254.     CSubclassWnd* pSubclassWnd;
  255.     while ((pSubclassWnd = Lookup(hwnd))!=NULL)
  256.         pSubclassWnd->HookWindow((HWND)NULL);    // (unhook)
  257. }
  258.  
  259. /////////////////
  260. // Find first hook associate with window
  261. //
  262. CSubclassWnd* CSubclassWndMap::Lookup(HWND hwnd)
  263. {
  264.     CSubclassWnd* pFound = NULL;
  265.     if (!CMapPtrToPtr::Lookup(hwnd, (void*&)pFound))
  266.         return NULL;
  267.     ASSERT_KINDOF(CSubclassWnd, pFound);
  268.     return pFound;
  269. }
  270.  
  271.